<?php

namespace WebApp\lib;


use ErrorException;
use PDO;
use PDOException;
use PDOStatement;
use WebApp\delegate\ICmsController;

class PDOManager implements ICmsController {
    //pdo对象
    private $_pdo = null;
    private $logDir;

    //私有构造
    public function __construct($host, $port, $dbName, $user, $pwd, $logDir) {
        try {
            if(!$logDir)
                $logDir = ROOT_DIR.'log/db.log';
            $this->logDir = $logDir;

            $this->setPdo(new PDO('mysql:host='.$host.';dbname=' . $dbName, $user, $pwd, array(
//                PDO::MYSQL_ATTR_INIT_COMMAND => "set names utf8mb4"
            )));

            $res = $this->getPdo()->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);

        } catch (PDOException $e) {
            throwError($e->getMessage());
        }
    }

    /**
     * @param $_sql
     * @return \PDOStatement
     */
    public function execute($_sql) {
        try {
            $_stmt = $this->getPdo()->prepare($_sql);
            $_stmt->execute();
            return $_stmt;
        } catch (PDOException  $e) {
            throwError('SQL语句：'.$_sql.'<br />错误信息：'.$e->getMessage());
        }

        return null;
    }

    private function makeWhere(&$where){
        if(is_array($where) && $where){
            $str = 'WHERE ';
            foreach ($where as $k => $v){

                if(is_array($v)){
                    $fuhao = $v[0];
                    $v = $v[1];
                }else{
                    $fuhao = '=';
                }
                $str .= "`$k`$fuhao:$k AND ";
                $where[':'.$k] = $v;
                unset($where[$k]);
            }
            $str = substr($str, 0, -5);
            return $str;
        }else{
            return $where;
        }
    }

    private function makeNeed($need){
        if(is_array($need) && $need){
            $str = '';
            foreach ($need as $k => $v){
                $str .= "`$v`,";
            }
            $str = substr($str, 0, -1);
            return $str;
        }else{
            return '*';
        }
    }

    public function makeGroup($group){
        if($group)
            return "GROUP BY $group";
        else
            return '';
    }

    public function makeLimit($limit){
        if($limit && is_array($limit))
            return "LIMIT " . $limit[0] . "," . $limit[1];
        else
            return '';
    }

    private function makeSetData($data){
        if($data && is_array($data)){
            $str = 'SET ';
            foreach ($data as $k=>$v){
                if($k == 'id'){
                    unset($data[$k]);
                    continue;
                }
                $str .= "`{$k}`=:{$k},";
                $data[":$k"] = $v;
                unset($data[$k]);
            }
            $str = substr($str, 0, -1);
            return [$str,$data];
        }else{
            return '';
        }
    }

    private function bindParams(PDOStatement $stmt, $data){
        if(is_array($data)){
            foreach ($data as $k => $v){
                if(is_array($v)){
                    foreach ($v as $key => $val){
                        $stmt->bindValue($key, $val);
                    }
                }else{
                    $stmt->bindValue($k, $v);
                }
            }
        }
    }

    public function get_obj($tableName='', $where='', $need = '', $group='', $sort=''){
        try {
            if(!$tableName)
                throwError('no table');

            $str = $this->makeWhere($where);
            if(!$str)
                throwError('cant find');

            $need = $this->makeNeed($need);
            $group = $this->makeGroup($group);
            $sort = $this->makeOrder($sort);
            $sql = "SELECT $need FROM $tableName {$str} $group {$sort}";

//            var_dump($sql,$where);exit;

            $_stmt = $this->getPdo()->prepare($sql);
            if(is_array($where)){
                $this->bindParams($_stmt, $where);
            }
            $_stmt->execute();

            $res = $_stmt->fetch();

            if(!is_array($res) || !$res){
                return [];
            }else{
                return $res;
            }
        } catch (PDOException  $e) {
            throwError($e->getMessage());
        } catch (ErrorException $e) {
            throwError($e->getMessage());
        }
        return null;
    }

    public function get_obj_count($tableName=null, $where=null){
        try {
            if(!$tableName)
                throwError('no table');

            $str = $this->makeWhere($where);
            $sql = "SELECT count(*) FROM {$tableName} {$str}";
//            var_dump($sql,$where);exit;
            $_stmt = $this->getPdo()->prepare($sql);
            $this->bindParams($_stmt, $where);
            $_stmt->execute();
            $res = $_stmt->fetch();
            if(!is_array($res) || !$res){
                return 0;
            }else{
                return array_values($res)[0];
            }
        } catch (PDOException  $e) {
            throwError($e->getMessage());
        } catch (ErrorException $e) {
            throwError($e->getMessage());
        }
        return null;
    }

    private function makeOrder($sort){
        if($sort){
            if(is_array($sort)){
                $sort = array_values($sort);
                return "ORDER BY `{$sort[0]}` ASC";
            }else{
                return "ORDER BY `{$sort}` DESC";
            }
        }
        return '';
    }

    public function get_obj_arr($tableName='', $where='',
                                $group='', $sort='',
                                $limit='', $need = ''){
        try {
            if(!$tableName)
                throwError('no table');

            $str = $this->makeWhere($where);
            $group = $this->makeGroup($group);
            $sort = $this->makeOrder($sort);
            $limit = $this->makeLimit($limit);
            $need = $this->makeNeed($need);
            $sql = "SELECT $need FROM $tableName {$str} $group {$sort} $limit";
//            if($tableName=='products'){
//                die($sql);
//            }

            $_stmt = $this->getPdo()->prepare($sql);
            if(is_array($where)){
                $this->bindParams($_stmt, $where);
            }
            $_stmt->execute();
            $res = $_stmt->fetchAll();

            if(!is_array($res) || !$res){
                return [];
            }else{
                return $res;
            }
        } catch (PDOException  $e) {
            throwError($e->getMessage());
        } catch (ErrorException $e) {
            throwError($e->getMessage());
        }
        return null;
    }

    public function add_obj($tableName='', $data = ''){
        try {
            if(!$tableName)
                throwError('no table');

            $arr = $this->makeSetData($data);
            $insert = $arr[0];
            $data = $arr[1];

            $sql = "INSERT INTO `$tableName` $insert";

//            var_dump($sql, $data);exit;

            $_stmt = $this->getPdo()->prepare($sql);
            $this->bindParams($_stmt, $data);
            $ret = $_stmt->execute();
            if(!$ret){
                file_put_contents($this->logDir,
                    json_encode(array("time"=>date("Y-m-d H:i:s"),
                        "dberr_insert"=>$_stmt->errorInfo(), "data"=>$data,
                        "sql"=>$sql,
                        "pdo_error"=>$this->getPdo()->errorInfo(),
                    ))."\n\n", FILE_APPEND);
            }
            return $ret;
        } catch (PDOException  $e) {
            throwError($e->getMessage());
        } catch (ErrorException $e) {
            throwError($e->getMessage());
        }
        return null;
    }

    public function remove_obj($tableName='', $where = '')
    {
        try {
            if(!$tableName)
                throwError('no table');

            $str = $this->makeWhere($where);
            if(!$str)
                throwError('cant remove');

            $sql = "DELETE FROM $tableName {$str}";
            $_stmt = $this->getPdo()->prepare($sql);
            $this->bindParams($_stmt, [$where]);

            $ret = $_stmt->execute();
            if(!$ret){
                file_put_contents($this->logDir,
                    json_encode(array("time"=>date("Y-m-d H:i:s"),
                        "dberr_delete"=>$_stmt->errorInfo(), "data"=>[$where],
                        "sql"=>$sql,
                        "pdo_error"=>$this->getPdo()->errorInfo(),
                    ))."\n\n", FILE_APPEND);
            }
            return $ret;
        } catch (PDOException  $e) {
            throwError($e->getMessage());
        } catch (ErrorException $e) {
            throwError($e->getMessage());
        }
        return null;
    }

    public function update_obj($tableName='', $where = '', $data = '')
    {
        try {
            if(!$tableName)
                throwError('no table');

            if(!$data)
                throwError('no need to update');

            $str = $this->makeWhere($where);
            if(!$str)
                throwError('cant update');

            $arr = $this->makeSetData($data);
            $set = $arr[0];
            $data = $arr[1];

            $sql = "UPDATE $tableName $set $str";

            $_stmt = $this->getPdo()->prepare($sql);
            $this->bindParams($_stmt, [$data,$where]);
            $ret = $_stmt->execute();
            if(!$ret){
                file_put_contents($this->logDir,
                    json_encode(array("time"=>date("Y-m-d H:i:s"),
                        "dblog_update"=>[$data,$where],
                        "sql"=>$sql,
                        "pdo_error"=>$this->getPdo()->errorInfo(),
                    ))."\n\n", FILE_APPEND);
            }
            return $ret;
        } catch (PDOException  $e) {
            throwError($e->getMessage());
        } catch (ErrorException $e) {
            throwError($e->getMessage());
        }
        return null;
    }

    public function get_obj_by_sql($sql){
        try{
            $_stmt = $this->getPdo()->prepare($sql);
            $_stmt->execute();
            return $_stmt->fetch();
        }catch (ErrorException $e){
            throwError($e->getMessage());
        }
        return null;
    }

    public function get_obj_arr_by_sql($sql){
        try{
            $_stmt = $this->getPdo()->prepare($sql);
            $_stmt->execute();
            return $_stmt->fetchAll();
        }catch (ErrorException $e){
            throwError($e->getMessage());
        }
        return null;
    }

    /**
     * @return PDO
     */
    public function getPdo()
    {
        return $this->_pdo;
    }

    /**
     * @param PDO $pdo
     */
    public function setPdo($pdo)
    {
        $this->_pdo = $pdo;
    }
}